#include <stdio.h>
#include <string.h>
#include "zip.h"

#define MAX_FILE_NUM 10

uint32_t table[ 256 ] ;

void init_table( ) {

  int i, j ;

  for( i = 0; i < 256; i++ ) {
    uint32_t a = i ;
    for( j = 0; j <= 7; j++ ) {
      uint32_t old_a = a ;
      a >>= 1 ;
      if( old_a & 1 ) a ^= 0xedb88320 ;
    }
    table[i] = a ;
  }

}

uint32_t crc32( FILE *fp ) {

  uint32_t a = ~0 ;
  int c ;

  fseek( fp, 0, SEEK_SET ) ;
  while( ( c = getc( fp ) ) != EOF ) {
    a ^= ( uint8_t ) c ;
    a = ( a >> 8 ) ^ table[ a & 0xff ] ;
  }
  return ~a ;

}

int main( int argc, char **argv ) {

  FILE *fp, *out ;
  unsigned int i ;
  unsigned int file_size ;
  int c ;
  ZIP_HEADER zip_headers[ MAX_FILE_NUM ] ;
  ZIP_CENTRAL_HEADER zip_central_headers[ MAX_FILE_NUM ] ;
  ZIP_END_HEADER zip_end_header ;
  unsigned int positions[ MAX_FILE_NUM ] ;
  unsigned int central_dir_size ;
  unsigned int central_dir_offset ;

  if( argc < 2 ) {
    printf( "usage : %s <output file> <input file1> <input file2> ... \n", argv[ 0 ] ) ;
    return -1 ;
  }

  if( ! ( out = fopen( argv[ 1 ], "wb" ) ) ) {
    printf( "can't open output file %s\n", argv[ 1 ] ) ;
    return -1 ;
  }

  init_table( ) ;

  for( i = 0; i < argc - 2; i++ ) {

    if( ! ( fp = fopen( argv[ i + 2 ], "rb" ) ) ) {
       printf( "can't open file %s\n", argv[ i ] ) ;
       return -1 ;
    }
    fseek( fp, 0, SEEK_END ) ;
    file_size = ftell( fp ) ;

    positions[ i ] = ftell( out ) ;

    // zip_headers[ i ].signature = { 0x50, 0x4b, 0x03, 0x04 } ;
    zip_headers[ i ].signature[ 0 ]     = 0x50 ;
    zip_headers[ i ].signature[ 1 ]     = 0x4b ;
    zip_headers[ i ].signature[ 2 ]     = 0x03 ;
    zip_headers[ i ].signature[ 3 ]     = 0x04 ;
    zip_headers[ i ].version            = 0x0a00 ;  // is it ok?
    zip_headers[ i ].flags              = 0x0000 ;  // is it ok?
    zip_headers[ i ].compression        = 0x0000 ;  // is it ok?
    zip_headers[ i ].dos_time           = 0x8045 ;  // temporary
    zip_headers[ i ].dos_date           = 0xae3e ;  // temporary
    zip_headers[ i ].crc32              = crc32( fp ) ;
    zip_headers[ i ].compressed_size    = file_size ;
    zip_headers[ i ].uncompressed_size  = file_size ;
    zip_headers[ i ].file_name_length   = strlen( argv[ i + 2 ] );
    zip_headers[ i ].extra_field_length = 0x0000 ;  // is it ok?

    // output zip_header
    fwrite( &zip_headers[ i ], sizeof( zip_headers[ i ] ), 1, out ) ;

    // output filename
    fputs( argv[ i + 2 ], out ) ;

    // output data
    fseek( fp, 0, SEEK_SET ) ; 
    while( ( c = getc( fp ) ) != EOF ) {
      putc( c, out ) ;
    }

    fclose( fp ) ;

  }

  central_dir_offset = ftell( out ) ;

  for( i = 0; i < argc - 2; i++ ) {

    // zip_central_headers[ i ].signature = { 0x50, 0x4b, 0x01, 0x02 } ;
    zip_central_headers[ i ].signature[ 0 ]     = 0x50 ;
    zip_central_headers[ i ].signature[ 1 ]     = 0x4b ;
    zip_central_headers[ i ].signature[ 2 ]     = 0x01 ;
    zip_central_headers[ i ].signature[ 3 ]     = 0x02 ;
    zip_central_headers[ i ].version_made       = 0x1400 ;  // is it ok?
    zip_central_headers[ i ].version            = zip_headers[ i ].version ;
    zip_central_headers[ i ].flags              = zip_headers[ i ].flags ;
    zip_central_headers[ i ].compression        = zip_headers[ i ].compression ;
    zip_central_headers[ i ].dos_time           = zip_headers[ i ].dos_time ;
    zip_central_headers[ i ].dos_date           = zip_headers[ i ].dos_date ;
    zip_central_headers[ i ].crc32              = zip_headers[ i ].crc32 ;
    zip_central_headers[ i ].compressed_size    = zip_headers[ i ].compressed_size;
    zip_central_headers[ i ].uncompressed_size  = zip_headers[ i ].uncompressed_size ;
    zip_central_headers[ i ].file_name_length   = zip_headers[ i ].file_name_length ;
    zip_central_headers[ i ].extra_field_length = zip_headers[ i ].extra_field_length ;
    zip_central_headers[ i ].file_comment_length = 0x0000 ;  // if comment not exists
    zip_central_headers[ i ].disk_number_start   = 0x0000 ;  // is it ok?
    zip_central_headers[ i ].internal_file_attributes = 0x0000 ; // is it ok?
    zip_central_headers[ i ].external_file_attributes = 0x00000000 ;  // is it ok?
    zip_central_headers[ i ].position = positions[ i ] ;

    // output zip_central_header
    fwrite( &zip_central_headers[ i ], sizeof( zip_central_headers[ i ] ), 1, out ) ;

    // output filename
    fputs( argv[ i + 2 ], out ) ;

  }

  // zip_end_header.signature = { 0x50, 0x4b, 0x05, 0x06 };
  zip_end_header.signature[ 0 ] = 0x50 ;
  zip_end_header.signature[ 1 ] = 0x4b ;
  zip_end_header.signature[ 2 ] = 0x05 ;
  zip_end_header.signature[ 3 ] = 0x06 ;

  zip_end_header.number_of_disks = 0x0000 ;  // is it ok?
  zip_end_header.disk_number_start = 0x0000 ;  // is it ok?
  zip_end_header.number_of_disk_entries = argc - 2 ;
  zip_end_header.number_of_entries = argc - 2 ;
  zip_end_header.central_dir_size = ftell( out ) - central_dir_offset ;
  zip_end_header.central_dir_offset = central_dir_offset ;
  zip_end_header.file_comment_length = 0x0000 ;  // if comment not exists 

  // output zip_end_header
  fwrite( &zip_end_header, sizeof( zip_end_header ), 1, out ) ;

  fclose( out ) ;

  return 0 ;

}

